package club.itstudio.itouc.service

import club.itstudio.itouc.config.ElectiveCoursecConfig
import club.itstudio.itouc.core.exception.ITOUCException
import club.itstudio.itouc.core.exception.AccountError
import club.itstudio.itouc.core.util.Ocr
import club.itstudio.itouc.core.util.mapper
import club.itstudio.itouc.entity.*
import club.itstudio.itouc.jw.*
import club.itstudio.itouc.util.Mysql
import club.itstudio.itouc.util.Redis
import com.fasterxml.jackson.module.kotlin.readValue
import com.github.kittinunf.fuel.Fuel
import com.github.kittinunf.fuel.core.FileDataPart
import com.github.kittinunf.fuel.core.Method
import org.springframework.scheduling.annotation.Async
import org.springframework.stereotype.Service
import java.io.File
import java.util.*


@Service
class JwOps(val jw: ParamsBuilder,
            val parser: Parser,
            val ecConfig: ElectiveCoursecConfig,
            val redis: Redis,
            val mysql: Mysql) {
    /**
     * 登录教务系统
     */
    fun loginJw(session: JwSession){
        var tryTimes = 0
        while (tryTimes < 3) {
            // 通过session获取验证码
            val captchaPath = jw.captcha(session)
            Ocr.imageProcess(captchaPath, captchaPath)

            val captchaResult = Fuel.upload("http://localhost:6000/b", method = Method.POST)
                    .add(FileDataPart(File(captchaPath), "image_file"))
                    .responseString().third.get()

            val captchaMap:Map<String, String> = mapper.readValue(captchaResult)

            val captcha = captchaMap["value"] ?: ""
            // 尝试登录
            val data = jw.login(session.userCode, session.password, captcha, session) with session
            // 解析结果
            val result:Map<String, String> = mapper.readValue(data)
            when (result["message"]) {
                "帐号或密码有误!" -> throw AccountError(message = result["message"] ?: "")
                "操作成功!" ->  {
                    // 删除识别成功的图片
                    File(captchaPath).delete()
                    return
                }
                else -> {
                    val file = File(captchaPath)
                    val fileName = file.name
                    file.renameTo(File("unrecognized/${captcha}_$fileName"))
                }
            }
            tryTimes++
        }
        throw AccountError(message = "登录失败，请重新尝试")
    }

    /**
     * 通过令牌获取有效的教务系统会话,若需要访问教务系统，使用此方法
     */
    fun getValidJwSession(token: String) = redis.getJwSession(token).also {
        // 刷新会话，确保会话有效
        it.refresh()
    }

    /**
     * 仅获取jwSession,用于判断用户身份
     */
    fun getJwSession(token: String) = redis.getJwSession(token)

    /**
     * 从教务系统获取年级、专业信息
     */
    fun getStuGradeSpecialtyInfoFromJw(session: JwSession): OUCStudent {
        val data = jw.gradeSpecialtyInfo() with session
        val dataJson: Map<String, String> = mapper.readValue(data)
        return dataJson["result"].let {
            val result: Map<String, String> = mapper.readValue(it ?: "")
            val grade = result["nj"] ?: ""
            val specialtyCode = result["zydm"] ?: ""
            OUCStudent(session.userCode, grade = grade, specialtyCode = specialtyCode)
        }
    }

    /**
     * 从教务系统获取专业列表
     */
    fun getSpecialtyList(year: String): List<Specialty> {
        val dataJson = jw.specialtyList(year).get()
        val specialtyJsonList = mapper.readValue<List<Map<String, String>>>(dataJson)
        val list = mutableListOf<Specialty>()
        specialtyJsonList.forEach {
            val code = it["code"] ?: ""
            val name = it["name"] ?: ""
            list.add(Specialty(name.substring(8),code, name.substring(1, 7)))
        }
        return list
    }

    /**
     * 获取选课的年级区间
     */
    fun electiveGradeRange(): IntArray {
        var nowYear = Calendar.getInstance().get(Calendar.YEAR)
        return IntArray(5) {
            nowYear--
        }
    }

    /**
     * 获取学生选课列表
     */
    fun getStuCourseList(jwSession: JwSession,
                         year: String = ecConfig.year,
                         semester: String = ecConfig.semester): List<StuCourse>{
        val data = jw.studentCourseList(year, semester, jwSession.userCode) with jwSession
        return parser.stuCourseList(data)
    }

    /**
     * 先从缓存中获取课表，找不到再从教务处获取
     */
    fun getCacheStuCourseList(token: String,
                              year: String,
                              semester: String): List<StuCourse> {
        var session = getJwSession(token)
        return try {
            redis.getStuCourse(session.userCode, year, semester)
        }catch (e: ITOUCException) {
            session = getValidJwSession(token)
            getStuCourseList(session, year, semester).also {
                redis.saveStuCourse(it, session.userCode, year, semester)
            }
        }
    }

    /**
     * 获取学生的毕业学分要求
     */
    fun getRequiredCredits(session: JwSession): List<Credit> {
        val data = jw.requiredCredit() with session
        return parser.requiredCredit(data)
    }

    /**
     * 获取成绩
     */
    fun getScoreList(session: JwSession): List<CourseScore> {
        val keyTime = parser.keyTimeParam(jw.keyTimeParam() with session)
        val data = jw.score(session.userCode, keyTime) with session
        return parser.scoreList(data)
    }
    /**
     * 异步更新成绩
     */
    @Async
    fun updateScore(session: JwSession) {
        session.refresh()
        redis.saveScore(session.userCode, getScoreList(session))
    }

    /**
     * 获取建议课表
     */
    fun getPlanCourse(session: JwSession, year: String, specialtyNum: String): List<CourseScore> {
        val data = jw.planCourse(session.userCode, year, specialtyNum) with session
        return parser.planCourseList(data)
    }

}
